package com.huawang.mdesign.model.service.inter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.huawang.business.command.CommandAddType;
import com.huawang.business.command.CommandParameter;
import com.huawang.business.constants.*;
import com.huawang.business.dto.*;
import com.huawang.business.dto.changeanalyse.ChangeAnalyseRuleInfo;
import com.huawang.business.entity.change.ChangeAnalysisData;
import com.huawang.business.entity.change.ChangeAnalysisResult;
import com.huawang.business.entity.change.ChangeModelResult;
import com.huawang.business.entity.change.ChangePropertyResult;
import com.huawang.business.exception.AbortExecutionException;
import com.huawang.business.service.changeanlysis.IChangeAnalysisBaseService;
import com.huawang.business.service.model.base.ElementModelInfoService;
import com.huawang.business.service.model.base.ElementNameService;
import com.huawang.business.service.model.base.ModelManagerConfig;
import com.huawang.business.service.model.base.UmlService;
import com.huawang.business.service.model.core.*;
import com.huawang.business.service.model.core.add.operation.CreateDiagram;
import com.huawang.business.service.model.core.delete.DeleteService;
import com.huawang.business.service.model.core.project.ProjectManager;
import com.huawang.business.service.model.core.property.UpdateModelFactory;
import com.huawang.business.service.transaction.TransactionServiceImpl;
import com.huawang.business.util.InstanceVoUtils;
import com.huawang.business.vo.ModelResultVo;
import com.huawang.business.vo.diagram.RelationMapNodeVo;
import com.huawang.core.definition.mof.UMLDefinitionConstants;
import com.huawang.core.definition.mof.cmof.reflection.impl.BasicRElement;
import com.huawang.core.definition.mof.instances.MetaClassInstance;
import com.huawang.core.semantics.entity.queryResult.MofResultContainer;
import com.huawang.core.semantics.entity.retrospectmap.SimpleNavigationCollection;
import com.huawang.core.semantics.entity.transaction.LocalTransaction;
import com.huawang.core.semantics.exception.LocalTransactionNotStartException;
import com.huawang.core.semantics.exception.TransactionNotExistingException;
import com.huawang.mdesign.model.service.dto.changeanalyse.*;
import com.huawang.mdesign.model.service.util.LogUtil;
import com.huawang.mdesign.model.service.vo.PageVo;
import com.huawang.mdesign.model.service.vo.changeanalyse.ChangeAnalyseRuleVo;
import com.huawang.mdesign.model.service.vo.changeanalyse.UploadChangeDataVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.huawang.business.constants.ModelActionName.ADD_MODEL;

/**
 * @author baibf
 * @description 变更分析Service
 * @date 2023/4/20 18:03
 **/
@Service
@Primary
@Slf4j
public class ChangeAnalyseService {

    public static final String ANALYSE_MAP_NAME = "变更影响分析追溯图";
    private static final String PROFILE_ID = "ChangeRuleProfile";

    @Autowired
    private DiagramMapService diagramMapService;

    @Autowired
    private ProjectManager projectManager;

    @Autowired
    private UmlService umlService;

    @Autowired
    private InternalUmlService internalUmlService;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private CreateDiagram createDiagram;

    @Autowired
    private ModelPropertyInfoService modelPropertyInfoService;

    @Autowired
    private InternalDiagramService internalDiagramService;

    @Autowired
    private ModelManagerConfig managerConfig;

    @Autowired
    private ObjectMapper defaultMapper;

    @Autowired
    private InternalSpecificationService internalSpecificationService;

    @Autowired
    private ElementNameService elementNameService;

    @Autowired
    private TransactionServiceImpl transactionService;

    @Autowired
    private DeleteService deleteService;

    @Autowired
    private IChangeAnalysisBaseService changeAnalysisBaseService;

    @Autowired
    private InternalPropertyService internalPropertyService;

    @Autowired
    private ElementModelInfoService elementModelInfoService;

    @NotNull
    public UploadChangeDataVo queryChangeDataToBeUpload(QueryUploadChangeDataDto queryUploadChangeDataDto) {
        LogUtil.printSerializeOnlineLogs("queryUploadChangeDataDto: {}", queryUploadChangeDataDto);
        final String projectId = queryUploadChangeDataDto.getProjectId();
        UploadChangeDataVo uploadChangeDataVo = new UploadChangeDataVo();
        uploadChangeDataVo.setProjectId(projectId);
        // 获取变更分析规则
        List<String> changeAnalyseRuleStr = queryChangeAnalyseRuleInfo(projectId);
        if (CollectionUtils.isEmpty(changeAnalyseRuleStr)) {
            return uploadChangeDataVo;
        }
        //获取变更影响结果
        List<ChangeModelResult> changeModelResult = changeAnalysisBaseService.getChangeModelResult(projectId);
        ChangeAnalysisResult changeAnalysisListData = changeAnalysisBaseService.getCalcChangeAnalysisData(projectId,
                changeAnalyseRuleStr, changeModelResult);
        uploadChangeDataVo.setAffectedPackageIds(new ArrayList<>(changeAnalysisListData.getAffectedPackageIds()));
        Map<String, ChangeAnalysisData> analysisDataMap =
                changeAnalysisListData.getChangeAnalysisData().stream()
                        .collect(Collectors.toMap(it -> it.getChangeModelResult().getInstanceId(), it -> it));
        List<ChangeRecordInfo> result = createChangeRecordInfoListByAnalysis(projectId,
                changeModelResult, analysisDataMap);
        uploadChangeDataVo.setChangeModelResult(result);
        return uploadChangeDataVo;
    }

    @NotNull
    public ModelResultVo createChangeAnalyseMap(AddChangeAnalyseMapDto dto) {
        LogUtil.printSerializeOnlineLogs("createChangeAnalyseMap -> dto: ", dto);
        final String projectId = dto.getProjectId();
        final String parentId = dto.getParentId();
        final MetaClassInstance root = internalUmlService.fetchExistInstance(dto.getInstanceId(), projectId);
        ModelResultVo modelResultVo = new ModelResultVo();

        LocalTransaction transaction = transactionService.createTransaction(projectId, ADD_MODEL);
        MetaClassInstance changeAnalyseMap;
        try {
            String mapDefaultName = getMapDefaultName(projectId, root, parentId);
            // 创建变更分析追溯图
            AddModelDTO addModelDto = new AddModelDTO();
            addModelDto.setProjectId(projectId);
            addModelDto.setParentId(parentId);
            addModelDto.setType(CommandAddType.MODEL.getCode());
            addModelDto.setMetaclass(UMLDefinitionConstants.Diagram);
            addModelDto.setApplyStereotypes(Collections.singletonList(UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap));
            addModelDto.setDefaultName(mapDefaultName);
            changeAnalyseMap = createDiagram.doHandler(addModelDto);

            List<UpdateModelDTO> updateModelDtos = new ArrayList<>();
            // 设置变更分析追溯图上下文追溯元素
            updateModelDtos.add(UpdateModelFactory.newInstance(projectId, changeAnalyseMap.getId(),
                    UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_contextElements, Collections.singletonList(root)));
            updateModelDtos.add(UpdateModelFactory.newInstance(projectId, changeAnalyseMap.getId(),
                    UMLDefinitionConstants.TableAndMatrix_DiagramMap_contextElement, Collections.singletonList(root)));
            List<UpdateModelDTO> updateRelationDtos =
                    internalDiagramService.createUpdateChangeAnalysePropertyDTO(projectId,
                            changeAnalyseMap, root, new HashSet<>(dto.getPropertyIds()));
            if (CollectionUtils.isNotEmpty(updateRelationDtos)) {
                updateModelDtos.addAll(updateRelationDtos);
            }

            // 设置变更属性
            if (CollectionUtils.isNotEmpty(dto.getPropertyIds())) {
                List<MetaClassInstance> properties = internalUmlService.fetchInstanceByIds(dto.getPropertyIds(),
                        projectId);
                updateModelDtos.add(UpdateModelFactory.newInstance(projectId, changeAnalyseMap.getId(),
                        UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_affectProperty, properties));
            }

            updateModelDtos.add(UpdateModelFactory.newInstance(projectId, changeAnalyseMap.getId(),
                    UMLDefinitionConstants.TableAndMatrix_DiagramMap_depth, 3));
            umlService.updateModelByBatch(updateModelDtos);
            transactionService.commit(projectId);
            modelResultVo.setInstanceId(changeAnalyseMap.getId());
            InstanceVoUtils.fillModelResultVo(modelResultVo, transaction);
        } catch (TransactionNotExistingException | LocalTransactionNotStartException e) {
            throw AbortExecutionException.build(ErrorCode.TRANSACTION_ERROR);
        } catch (Exception e) {
            transactionService.rollBack(projectId, transaction);
            throw AbortExecutionException.build(ErrorCode.ADD_MODEL_ERROR, e);
        }

        return modelResultVo;
    }

    @NotNull
    private String getMapDefaultName(String projectId, MetaClassInstance root, String parentId) {
        LogUtil.printOnlineLogs("projectId: {}, root: {}, parentId: {}", projectId, root.getId(), parentId);
        String name = internalUmlService.queryStringProperty(projectId, root, UMLDefinitionConstants.NamedElement_name);
        String mapDefaultName;
        if (StringUtils.isNotEmpty(name)) {
            mapDefaultName = elementNameService.fetchDefaultName(projectId,
                    elementModelInfoService.getModelInfoKeyByStereotypeId(UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap),
                    parentId, name + "-" + ANALYSE_MAP_NAME, null);
        } else {
            mapDefaultName = ANALYSE_MAP_NAME;
        }
        return mapDefaultName;
    }

    @Nullable
    public RelationMapNodeVo queryChangeAnalyseMap(String projectId, String mapId, String rootId, Integer level) {
        LogUtil.printOnlineLogs("project: {}, mapId: {}, rootId：{}, level: {}", projectId, mapId, rootId, level);
        MetaClassInstance changeAnalyseMap = internalUmlService.fetchExistInstance(mapId, projectId);
        if (rootId == null) {
            MetaClassInstance context = internalUmlService.queryInstanceProperty(projectId,
                    changeAnalyseMap, UMLDefinitionConstants.TableAndMatrix_DiagramMap_contextElement);
            if (context == null) {
                log.error("上下文元素不存在");
                return new RelationMapNodeVo();
            }

            return diagramMapService.queryAnalyseMapTree(projectId, mapId, context, level);
        } else {
            MetaClassInstance root = internalUmlService.fetchExistInstance(rootId, projectId);
            return diagramMapService.queryAnalyseMapTree(projectId, mapId, root, level);
        }
    }

    @NotNull
    public ModelResultVo markChangeNode(MarkChangeNodeDto markChangeNodeDto) {
        LogUtil.printSerializeOnlineLogs("markChangeNodeDto: ", markChangeNodeDto);
        final ModelResultVo modelResultVo = new ModelResultVo();
        final String projectId = markChangeNodeDto.getProjectId();
        final String mapId = markChangeNodeDto.getMapId();
        final MetaClassInstance diagramMap = internalUmlService.fetchExistInstance(mapId, projectId);
        final List<MetaClassInstance> instances = internalUmlService.fetchInstanceByIds(markChangeNodeDto.getInstanceIds(),
                projectId);

        LocalTransaction transaction = transactionService.createTransaction(
                projectId, ModelActionName.UPDATE_PROPERTY);
        try {
            if (Objects.equals(markChangeNodeDto.getChangeMark(), AnalyseNodeStateEnum.MAY_EFFECT.value())) {
                umlService.removeMultipleModel(projectId, markChangeNodeDto.getMapId(),
                        UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_notEffectElement,
                        instances);
                umlService.removeMultipleModel(projectId, markChangeNodeDto.getMapId(),
                        UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_confirmEffectElement,
                        instances);
                umlService.appendMultipleModel(projectId, mapId,
                        UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_mayEffectElement,
                        new ArrayList<>(instances));
                createAffectRecord(projectId, diagramMap, instances,
                        UMLDefinitionConstants.Customization_AffectRecord_mayEffect,
                        UMLDefinitionConstants.Customization_AffectRecord_mayTime);
            } else if (Objects.equals(markChangeNodeDto.getChangeMark(), AnalyseNodeStateEnum.CONFIRM_EFFECT.value())) {
                umlService.removeMultipleModel(projectId, markChangeNodeDto.getMapId(),
                        UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_notEffectElement,
                        instances);
                umlService.removeMultipleModel(projectId, markChangeNodeDto.getMapId(),
                        UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_mayEffectElement,
                        instances);
                umlService.appendMultipleModel(projectId, mapId,
                        UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_confirmEffectElement, new ArrayList<>(instances));
                createAffectRecord(projectId, diagramMap, instances,
                        UMLDefinitionConstants.Customization_AffectRecord_confirmEffect,
                        UMLDefinitionConstants.Customization_AffectRecord_confirmTime);
            } else if (Objects.equals(markChangeNodeDto.getChangeMark(), AnalyseNodeStateEnum.NOT_EFFECT.value())) {
                umlService.removeMultipleModel(projectId, markChangeNodeDto.getMapId(),
                        UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_confirmEffectElement,
                        instances);
                umlService.removeMultipleModel(projectId, markChangeNodeDto.getMapId(),
                        UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_mayEffectElement,
                        instances);
                umlService.appendMultipleModel(projectId, mapId,
                        UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_notEffectElement,
                        new ArrayList<>(instances));
                createAffectRecord(projectId, diagramMap, instances,
                        UMLDefinitionConstants.Customization_AffectRecord_notEffect,
                        UMLDefinitionConstants.Customization_AffectRecord_notTime);
            } else {
                log.info("变更分析节点状态{}不是MayEffect、ConfirmEffect或NotEffect", markChangeNodeDto.getChangeMark());
            }
            transactionService.commit(projectId);
            modelResultVo.setInstanceId(mapId);
            InstanceVoUtils.fillModelResultVo(modelResultVo, transaction);
        } catch (TransactionNotExistingException | LocalTransactionNotStartException e) {
            throw AbortExecutionException.build(ErrorCode.TRANSACTION_ERROR);
        } catch (Exception e) {
            transactionService.rollBack(projectId, transaction);
            throw AbortExecutionException.build(ErrorCode.PROPERTY_UPDATE_ERROR, e);
        }

        return modelResultVo;
    }

    private void createAffectRecord(String projectId, MetaClassInstance diagramMap, List<MetaClassInstance> instances, String stateKey, String timeKey) {
        LogUtil.printOnlineLogs("projectId: {}, diagramMap: {}, stateKey: {}, timeKey: {}", projectId, diagramMap.getId(),
                stateKey, timeKey);
        MetaClassInstance metaAffectedElement =
                internalUmlService.fetchInstance(UMLDefinitionConstants.Customization_AffectedElement, projectId);
        if (metaAffectedElement != null) {
            MofResultContainer container = internalUmlService.queryProperty(projectId,
                    internalUmlService.getQueryPropertyForm(instances,
                            UMLDefinitionConstants.Element_appliedStereotype));
            List<MetaClassInstance> noStereotypeInstances =
                    instances.stream().filter(it -> !container.queryListInstanceProperty(it.getId(),
                                    UMLDefinitionConstants.Element_appliedStereotype).contains(metaAffectedElement))
                            .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(noStereotypeInstances)) {
                umlService.appendModel(projectId, noStereotypeInstances.stream().map(BasicRElement::getId).collect(Collectors.toList()),
                        UMLDefinitionConstants.Element_appliedStereotype,
                        metaAffectedElement);
            }
        }

        List<UpdateModelDTO> dtos = new ArrayList<>();
        AddModelDTO addRecord = new AddModelDTO();
        addRecord.setMetaclass(UMLDefinitionConstants.Class);
        addRecord.setType(CommandAddType.MODEL.getCode());
        addRecord.setApplyStereotypes(Stream.of(UMLDefinitionConstants.Customization_AffectRecord)
                .collect(Collectors.toList()));
        addRecord.setProjectId(projectId);
        addRecord.setCustomName("元素受影响记录");
        MetaClassInstance parent = umlService.queryValidParent(diagramMap, UMLDefinitionConstants.Customization_AffectRecord, projectId);
        if (parent != null) {
            addRecord.setParentId(parent.getId());
        } else {
            addRecord.setParentId(diagramMap.getId());
        }
        final MetaClassInstance affectRecord = umlService.addModel(addRecord);
        ModelPropertyInfo mapModel = modelPropertyInfoService.createSimpleModelPropertyInfo(projectId, diagramMap,
                Stream.of(UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_contextElements,
                        UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_affectProperty).collect(Collectors.toList()));
        dtos.add(UpdateModelFactory.newInstance(projectId, affectRecord.getId(),
                UMLDefinitionConstants.Customization_AffectRecord_property,
                mapModel.queryListInstanceValue(UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_affectProperty)));
        dtos.add(UpdateModelFactory.newInstance(projectId, affectRecord.getId(),
                UMLDefinitionConstants.Customization_AffectRecord_element,
                mapModel.queryListInstanceValue(UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_contextElements)));
        dtos.add(UpdateModelFactory.newInstance(projectId, affectRecord.getId(),
                stateKey,
                true));
        dtos.add(UpdateModelFactory.newInstance(projectId, affectRecord.getId(),
                timeKey,
                String.valueOf(System.currentTimeMillis())));
        dtos.add(UpdateModelFactory.newInstance(projectId, affectRecord.getId(),
                UMLDefinitionConstants.Customization_AffectRecord_version,
                projectManager.getValidProject(projectId).getVersion()));
        umlService.updateModelByBatch(dtos);
        umlService.appendModel(projectId, instances.stream().map(BasicRElement::getId).collect(Collectors.toList()),
                UMLDefinitionConstants.Customization_AffectedElementRecords, affectRecord);
    }

    @NotNull
    public List<ChangeMarkInfo> queryChangeNodeInfo(QueryChangeNodeInfoDTO dto) {
        LogUtil.printSerializeOnlineLogs("queryChangeNodeInfo -> dto: ", dto);
        final String projectId = dto.getProjectId();
        final List<String> instanceIds = dto.getInstanceIds();
        final String mapId = dto.getMapId();
        MetaClassInstance changeAnalyseMap = internalUmlService.fetchInstance(mapId, projectId);
        if (Objects.isNull(changeAnalyseMap) || CollectionUtils.isEmpty(instanceIds)) {
            return Collections.emptyList();
        }
        EffectElementInfo effectElementInfo = queryEffectElements(projectId, changeAnalyseMap);
        return instanceIds.stream().map(id -> {
            ChangeMarkInfo changeMarkInfo = new ChangeMarkInfo();
            changeMarkInfo.setInstanceId(id);
            if (effectElementInfo.getConfirmEffectElements().contains(id)) {
                changeMarkInfo.setChangeMark("ConfirmEffect");
            } else if (effectElementInfo.getNotEffectElements().contains(id)) {
                changeMarkInfo.setChangeMark("NotEffect");
            } else if (effectElementInfo.getMayEffectElement().contains(id)) {
                changeMarkInfo.setChangeMark("MayEffect");
            } else {
                log.info("影响元素、非影响元素或可能影响元素均不包含{}", id);
            }
            return changeMarkInfo;
        }).collect(Collectors.toList());
    }

    @NotNull
    public EffectElementInfo queryEffectElements(@NotNull String projectId, @NotNull MetaClassInstance changeAnalyseMap) {
        LogUtil.printOnlineLogs("projectId: {}, changeAnalyseMap: {}", projectId, changeAnalyseMap);
        ModelPropertyInfo relationMapModel = modelPropertyInfoService.createSimpleModelPropertyInfo(projectId,
                changeAnalyseMap,
                Stream.of(UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_confirmEffectElement,
                                UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_notEffectElement,
                                UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_mayEffectElement)
                        .collect(Collectors.toList()));
        Set<String> confirmEffectElements = relationMapModel.queryListInstanceValue(
                        UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_confirmEffectElement).stream()
                .map(BasicRElement::getId).collect(Collectors.toSet());
        Set<String> notEffectElements =
                relationMapModel.queryListInstanceValue(
                                UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_notEffectElement).stream()
                        .map(BasicRElement::getId).collect(Collectors.toSet());
        Set<String> mayEffectElement =
                relationMapModel.queryListInstanceValue(
                                UMLDefinitionConstants.TableAndMatrix_ChangeAnalysisMap_mayEffectElement).stream()
                        .map(BasicRElement::getId).collect(Collectors.toSet());
        return new EffectElementInfo(confirmEffectElements, notEffectElements, mayEffectElement);
    }

    @NotNull
    public ModelResultVo addRulePackage(AddRulePackageDto dto) {
        LogUtil.printSerializeOnlineLogs("addRulePackage -> dto: ", dto);
        final ModelResultVo modelResultVo = new ModelResultVo();
        final String projectId = dto.getProjectId();
        MetaClassInstance ruleProfile = internalUmlService.fetchInstance(PROFILE_ID, projectId);


        LocalTransaction transaction = transactionService.createTransaction(
                projectId, ModelActionName.UPDATE_PROPERTY);
        try {
            if (ruleProfile == null) {
                CommandParameter parameter = CommandParameter.builder()
                        .parentId(projectManager.getValidProject(projectId).getRootModel())
                        .typeId(UMLDefinitionConstants.Profile)
                        .type(CommandAddType.MODEL)
                        .projectId(projectId)
                        .defaultName("变更规则包")
                        .instanceId(PROFILE_ID).build();
                umlService.addModel(parameter);
            }

            transactionService.commit(projectId);
            InstanceVoUtils.fillModelResultVo(modelResultVo, transaction);
        } catch (TransactionNotExistingException | LocalTransactionNotStartException e) {
            throw AbortExecutionException.build(ErrorCode.TRANSACTION_ERROR);
        } catch (Exception e) {
            transactionService.rollBack(projectId, transaction);
            throw AbortExecutionException.build(ErrorCode.PROPERTY_UPDATE_ERROR, e);
        }

        return modelResultVo;
    }

    /**
     * 更新变更规则
     *
     * @param dto
     * @return com.huawang.mdesign.model.service.vo.ModelResultVo
     * @author xz
     * @date 2023/09/12 14:37
     */
    @NotNull
    public ModelResultVo updateRule(ChangeAnalyseRuleDto dto) {
        LogUtil.printSerializeOnlineLogs("updateRule -> dto: ", dto);
        final ModelResultVo modelResultVo = new ModelResultVo();
        final String projectId = dto.getProjectId();
        MetaClassInstance ruleProfile = internalUmlService.fetchInstance(PROFILE_ID, projectId);
        if (ruleProfile == null) {
            throw AbortExecutionException.build(ErrorCode.NO_RULE_PROFILE);
        }

        if (CollectionUtils.isEmpty(dto.getAddRules()) && CollectionUtils.isEmpty(dto.getUpdateRules()) && CollectionUtils.isEmpty(dto.getDeleteRules())) {
            log.error("无规则变化");
            throw AbortExecutionException.build(ErrorCode.NO_MODIFY_ROLE);
        }

        LocalTransaction transaction = transactionService.createTransaction(
                projectId, ModelActionName.UPDATE_PROPERTY);
        try {
            List<UpdateModelDTO> updateModelDTOS =
                    new ArrayList<>(dto.getAddRules().size() + dto.getUpdateRules().size());
            if (CollectionUtils.isNotEmpty(dto.getAddRules())) {
                dto.getAddRules().forEach(it -> {
                    if (it.getRuleId() == null) {
                        it.setRuleId(ModelConstantURI.initInstanceId());
                    }
                });
                List<CommandParameter> addModelDTOS = dto.getAddRules().stream().map(it -> CommandParameter.builder()
                        .type(CommandAddType.MODEL)
                        .typeId(UMLDefinitionConstants.Class)
                        .projectId(projectId)
                        .instanceId(it.getRuleId())
                        .parentId(ruleProfile.getId())
                        .applyStereotypes(Stream.of(UMLDefinitionConstants.Customization_ChangeRule)
                                .collect(Collectors.toList()))
                        .build()).collect(Collectors.toList());
                umlService.addModelByBatch(addModelDTOS);
                updateModelDTOS.addAll(makeRuleBodyUpdateDTO(dto.getAddRules(), projectId));
            }

            if (CollectionUtils.isNotEmpty(dto.getUpdateRules())) {
                List<UpdateModelDTO> updateRules = makeRuleBodyUpdateDTO(dto.getUpdateRules(), projectId);
                updateModelDTOS.addAll(updateRules);
            }

            if (CollectionUtils.isNotEmpty(dto.getDeleteRules())) {
                deleteService.deleteModels(projectId, dto.getDeleteRules().stream().map(ChangeAnalyseRuleInfo::getRuleId).collect(Collectors.toList()));
            }

            umlService.updateModelByBatch(updateModelDTOS);
            transactionService.commit(projectId);
            InstanceVoUtils.fillModelResultVo(modelResultVo, transaction);
        } catch (TransactionNotExistingException | LocalTransactionNotStartException e) {
            throw AbortExecutionException.build(ErrorCode.TRANSACTION_ERROR);
        } catch (Exception e) {
            transactionService.rollBack(projectId, transaction);
            throw AbortExecutionException.build(ErrorCode.PROPERTY_UPDATE_ERROR, e);
        }

        return modelResultVo;
    }

    @NotNull
    private List<UpdateModelDTO> makeRuleBodyUpdateDTO(List<ChangeAnalyseRuleInfo> rules, String projectId) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, rules: {}", projectId, rules);
        return rules.stream().map(it -> {
            try {
                if (CollectionUtils.isEmpty(it.getMetaChainNavigations())) {
                    it.setMetaChainNavigations(Stream.of(DiagramConstants.DEFAULT_NAVIGATION).collect(Collectors.toList()));
                }
                String rule = defaultMapper.writeValueAsString(it);
                return UpdateModelFactory.newInstance(projectId, it.getRuleId(),
                        UMLDefinitionConstants.Customization_ChangeRule_Rule_body, rule);
            } catch (JsonProcessingException e) {
                log.error("变更规则转换失败:{}", it);
            }

            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    @NotNull
    public ChangeAnalyseRuleVo queryRule(String projectId) {
        LogUtil.printOnlineLogs("queryRule -> projectId: ", projectId);
        List<ChangeAnalyseRuleInfo> ruleInfos = queryChangeAnalyseRuleInfo(projectId)
                .stream().map(it -> {
                    ChangeAnalyseRuleInfo info = new ChangeAnalyseRuleInfo();
                    try {
                        info = objectMapper.readValue(it, ChangeAnalyseRuleInfo.class);
                    } catch (JsonProcessingException e) {
                        log.error("json转换ChangeAnalyseRuleInfo失败:{}", it);
                    }
                    return info;
                }).collect(Collectors.toList());
        ChangeAnalyseRuleVo vo = new ChangeAnalyseRuleVo();
        //兼容旧数据
        ruleInfos.forEach(changeAnalyseRuleInfo -> {
            List<SimpleNavigationCollection> simpleNavigationCollections = changeAnalyseRuleInfo.getSimpleNavigationCollections();
            if (CollectionUtils.isEmpty(simpleNavigationCollections) &&
                    CollectionUtils.isNotEmpty(changeAnalyseRuleInfo.getSimpleNavigations())) {
                SimpleNavigationCollection simpleNavigationCollection = InstanceVoUtils.createSimpleNavigationCollection(changeAnalyseRuleInfo
                        .getSimpleNavigations());
                simpleNavigationCollections.add(simpleNavigationCollection);
            }
        });
        vo.setRuleInfos(ruleInfos);
        return vo;
    }

    @NotNull
    private List<String> queryChangeAnalyseRuleInfo(String projectId) {
        LogUtil.printOnlineLogs("queryChangeAnalyseRuleInfo -> projectId: ", projectId);
        MetaClassInstance ruleProfile = internalUmlService.fetchInstance(PROFILE_ID, projectId);
        if (ruleProfile == null) {
            log.warn("变更分析规则包不存在");
            return Collections.emptyList();
        }
        List<MetaClassInstance> rules = internalUmlService.getChildren(ruleProfile, false, projectId);
        List<ModelPropertyInfo> ruleModels = modelPropertyInfoService.createModelPropertyInfoAndTypeList(projectId,
                rules,
                Stream.of(UMLDefinitionConstants.Customization_ChangeRule_Rule_body).collect(Collectors.toList()));
        return ruleModels.stream().map(it -> {
            String body = it.queryStringValue(UMLDefinitionConstants.Customization_ChangeRule_Rule_body);
            if (StringUtils.isEmpty(body)) {
                log.warn("{}的Rule_body为空", it.getInstanceId());
                return null;
            }
            return body;
        }).collect(Collectors.toList());
    }

    @NotNull
    public PageVo<ChangeRecordInfo> queryChangeRecord(String projectId, Integer page, Integer size) {
        LogUtil.printOnlineLogs("projectId: {}, page: {}, size: {}", projectId, page, size);
        PageVo<ChangeRecordInfo> pageVo = new PageVo<>();
        List<ChangeRecordInfo> changeRecordInfos = queryChangeRecord(projectId);
        if (!changeRecordInfos.isEmpty()) {
            List<ChangeRecordInfo> data = new ArrayList<>();
            int startIndex = (page - 1) * size;
            int endIndex = page * size;
            if (endIndex > changeRecordInfos.size()) {
                endIndex = changeRecordInfos.size();
            }
            if (startIndex <= changeRecordInfos.size()) {
                data = changeRecordInfos.stream().skip(startIndex).limit(endIndex).collect(Collectors.toList());
            }
            pageVo.setData(data);
        } else {
            pageVo.setData(changeRecordInfos);
        }
        pageVo.setPage(page);
        pageVo.setSize(size);
        return pageVo;
    }

    @NotNull
    private List<ChangeRecordInfo> queryChangeRecord(String projectId) {
        LogUtil.printOnlineLogs("queryChangeRecord -> projectId: ", projectId);
        List<ChangeModelResult> changeModelResult = changeAnalysisBaseService.getChangeModelResult(projectId);
        if (CollectionUtils.isEmpty(changeModelResult)) {
            log.info("无变更记录");
            return new ArrayList<>();
        }

        return createChangeRecordInfoListByAnalysis(projectId,
                changeModelResult, new HashMap<>());

    }

    @NotNull
    public List<ChangeRecordInfo> createChangeRecordInfoListByAnalysis(String projectId,
                                                                       List<ChangeModelResult> changeModelResult,
                                                                       @NotNull Map<String, ChangeAnalysisData> analysisDataMap) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, changeModelResult: {}, analysisDataMap: {}", projectId,
                changeModelResult, analysisDataMap);
        final List<String> propertyNames = changeModelResult.stream().flatMap(it ->
                it.getChangeResultMap().stream()).map(ChangePropertyResult::getPropertyName).collect(Collectors.toList());
        final Map<String, MetaClassInstance> instanceMap = this.internalUmlService.fetchInstances(propertyNames, projectId);
        final Map<String, ModelPropertyInfo> models = modelPropertyInfoService.createModelPropertyInfoMap(
                projectId, new ArrayList<>(instanceMap.values()),
                Stream.of(UMLDefinitionConstants.StructuralFeature_isReadOnly, UMLDefinitionConstants.NamedElement_name,
                                UMLDefinitionConstants.TypedElement_type)
                        .collect(Collectors.toList()));
        final List<String> changeValues = changeModelResult.stream().flatMap(it -> it.getChangeResultMap().stream().flatMap(result ->
                Stream.concat(result.getChangeAfterValue().stream(), result.getChangeBeforeValue().stream())
        )).distinct().collect(Collectors.toList());
        final Map<String, MetaClassInstance> valueInstanceMap = this.internalUmlService.fetchInstances(changeValues, projectId);
        final Map<String, ValueSpecificationInfo> stringValueSpecificationInfoMap = internalSpecificationService
                .queryValueFromValueSpecification(projectId, new ArrayList<>(valueInstanceMap.values()));
        return changeModelResult.stream().map(it -> {
            ChangeRecordInfo info = new ChangeRecordInfo();
            info.setInstanceId(it.getInstanceId());
            List<PropertyChangeInfo> infos = it.getChangeResultMap().stream().map(changeResult -> {
                PropertyChangeInfo changeInfo = new PropertyChangeInfo();
                PropertyConfig config = managerConfig.getPropertyConfigs().get(changeResult.getPropertyName());
                changeInfo.setPropertyId(changeResult.getPropertyName());
                if (config == null) {
                    MetaClassInstance propertyInstance = instanceMap.get(changeResult.getPropertyName());
                    if (Objects.isNull(propertyInstance)) {
                        return null;
                    }
                    config = internalPropertyService.initStereotypeProperty(projectId, models.get(propertyInstance.getId()));
                }

                ChangePropertyValue oldVo = new ChangePropertyValue();
                ChangePropertyValue newVo = new ChangePropertyValue();
                BeanUtils.copyProperties(config, oldVo);
                BeanUtils.copyProperties(config, newVo);
                fillValue(projectId, newVo, changeResult.getChangeAfterValue(), changeResult.isPropertyValueSpecification(),
                        valueInstanceMap, stringValueSpecificationInfoMap);
                fillValue(projectId, oldVo, changeResult.getChangeBeforeValue(), changeResult.isPropertyValueSpecification(),
                        valueInstanceMap, stringValueSpecificationInfoMap);
                changeInfo.setNewValue(newVo);
                changeInfo.setOldValue(oldVo);
                changeInfo.setChangeType(it.getChangeType());
                return changeInfo;
            }).collect(Collectors.toList());
            info.setPropertyChangeInfos(infos);
            info.setAffectedPackageIds(getAffectedPackageIds(it, analysisDataMap));
            return info;
        }).collect(Collectors.toList());
    }

    @NotNull
    public Set<String> getAffectedPackageIds(ChangeModelResult result,
                                             @NotNull Map<String, ChangeAnalysisData> analysisDataMap) {
        LogUtil.printSerializeOnlineLogs("result: {}, analysisDataMap: {}", result, analysisDataMap);
        if (analysisDataMap.containsKey(result.getInstanceId())) {
            Map<String, List<String>> affectedPackageMap = analysisDataMap.get(result.getInstanceId()).getAffectedPackageMap();
            if (affectedPackageMap != null) {
                return affectedPackageMap.keySet();
            }
        }

        return new HashSet<>();
    }

    private void fillValue(String projectId, ChangePropertyValue config, List<String> values,
                           boolean isValueSpecification, Map<String, MetaClassInstance> valueInstanceMap,
                           Map<String, ValueSpecificationInfo> stringValueSpecificationInfoMap) {
        LogUtil.printSerializeOnlineLogs("projectId: {}, config: {}, values: {}, isValueSpecification: {}, valueInstanceMap: {}, stringValueSpecificationInfoMap: {}",
                projectId, config, values, isValueSpecification, valueInstanceMap, stringValueSpecificationInfoMap);
        if (config.getDataType() == null) {
            config.setDataType(internalUmlService.queryPropertyDataType(projectId, config.getKey()));
        }

        if (CollectionUtils.isNotEmpty(values) && isValueSpecification) {
            MetaClassInstance instance = valueInstanceMap.get(values.get(0));
            if (instance != null) {
                ValueSpecificationInfo valueSpecificationInfo = stringValueSpecificationInfoMap.get(instance.getId());
                config.setValueSpecificationInfo(valueSpecificationInfo);
            }
        }

        config.setValue(values);
    }
}
